﻿using AuthCenter.Data.Dto;
using AuthCenter.Data.Model._202411;
using AuthCenter.Tools.Password;
using Dapper;
using ExcelConfig;
using Google.Protobuf.WellKnownTypes;
using SanlieHash;
using SanlieHash.MessageDigest;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace AuthCenter.Data.Database
{
    public class AuthDeviceDB : IDisposable
    {
        internal IRepo repo;
        public AuthDeviceDB()
        {
            var connectstring = AppConfig.Text("auth_device");
            repo = new MysqlRepo(connectstring);
        }

        public Guid MakeDevice(Guid userid, string ip, string desc)
        {
            repo.Open();
            var type = FastHash.GetMD5(desc+ip);
            var device = new Device();
            device.DeviceType = type;
            device.UserID = userid;
            device.IP = ip;

            device.DeviceID = repo.Connection.QueryFirstOrDefault<Guid>("select DeviceID from device where UserID=@UserID and DeviceType=@DeviceType;", device);

            if (device.DeviceID == Guid.Empty)
            {
                device.DeviceDESC = desc;
                device.DeviceID = Guid.NewGuid();

                using (var transaction = repo.Connection.BeginTransaction())
                {
                    try
                    {
                        repo.Connection.Execute("insert into device (DeviceID, UserID, DeviceDESC, DeviceType, IP) values (@DeviceID, @UserID, @DeviceDESC, @DeviceType, @IP);", device, transaction);
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        // 发生异常时回滚事务
                        transaction.Rollback();
                        // 处理异常
                        Console.WriteLine(ex.Message);
                    }
                }
            }
            return device.DeviceID;
        }
        public TokenKey? GetTokenKey(Guid onlineId)
        {
            repo.Open();
            var output = new TokenKey();
            output.OnlineID = onlineId;
            output = repo.Connection.QueryFirstOrDefault<TokenKey>("select Secret, ExpTime from online_device where OnlineID=@OnlineID;", output);

            return output;
        }
        public TokenKey GetTokenKey(Guid deviceId, Guid appId)
        {
            repo.Open();
            var online = new OnlineDevice();
            var output = new TokenKey();
            online.AppID = appId;
            online.DeviceID = deviceId;
            var expSec = double.Parse(AppConfig.Text("登录有效期"));
            var old = repo.Connection.QueryFirstOrDefault<OnlineDevice>("select OnlineID, SignTime, ExpTime, Secret from online_device where DeviceID=@DeviceID and AppID=@AppID;", online);
            if (old != null)
            {
                online.OnlineID = old.OnlineID;
                if (old.ExpTime < DateTime.UtcNow)
                {
                    //过期, 重新生成密钥
                    online.SignTime = DateTime.UtcNow;
                    online.Secret = PassGen.MakePassword(128);
                }
                else
                {
                    online.SignTime = old.SignTime;
                    online.Secret = old.Secret;
                }
                online.ExpTime = DateTime.UtcNow.AddSeconds(expSec);
            }
            else
            {
                online.OnlineID = Guid.NewGuid();
                online.SignTime = DateTime.UtcNow;
                online.ExpTime = DateTime.UtcNow.AddSeconds(expSec);
                online.Secret = PassGen.MakePassword(128);
            }

            using (var transaction = repo.Connection.BeginTransaction())
            {
                try
                {
                    if (old != null)
                    {
                        repo.Connection.Execute("update online_device set ExpTime=@ExpTime, Secret=@Secret, SignTime=@SignTime where OnlineID=@OnlineID;", online, transaction);
                    }
                    else
                    {
                        repo.Connection.Execute("insert into online_device (OnlineID, DeviceID, SignTime, AppID, ExpTime, Secret) values (@OnlineID, @DeviceID, @SignTime, @AppID, @ExpTime, @Secret);", online, transaction);
                    }
                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    // 发生异常时回滚事务
                    transaction.Rollback();
                    // 处理异常
                    Console.WriteLine(ex.Message);
                }
            }
            output.OnlineID = online.OnlineID;
            output.Secret = online.Secret;
            return output;
        }
        public string? ReadOnlineDeviceType(Guid onlineId)
        {
            repo.Open();
            var deviceType = repo.Connection.QueryFirstOrDefault<string>("select b.DeviceType from online_device as a join device as b on a.DeviceID = b.DeviceID where OnlineID=@OnlineID;", new { OnlineID = onlineId });
            return deviceType;
        }
        public IList<OnlineDeviceInfo> GetOnlineDevice(Guid userId, Guid appId)
        {
            repo.Open();

            var onlines = repo.Connection.Query<OnlineDeviceInfo>("select a.OnlineID, b.IP, b.DeviceDESC, a.SignTime, a.ExpTime from online_device as a join device as b on a.DeviceID = b.DeviceID where a.AppID=@AppID and b.UserID=@UserID and a.ExpTime > @Now", new { AppID = appId, UserID = userId, Now=DateTime.UtcNow });
            return onlines.OrderByDescending(t=>t.SignTime).ToList();
        }
        public void ExpireOnline(Guid onlineId)
        {
            repo.Open();

            var exptime = repo.Connection.QueryFirstOrDefault<DateTime>("select ExpTime from online_device where OnlineID=@OnlineID;", new {OnlineID=onlineId});

            if (exptime > DateTime.UtcNow) {
                exptime = DateTime.UtcNow.AddMilliseconds(-1);
                using (var transaction = repo.Connection.BeginTransaction())
                {
                    try
                    {
                        repo.Connection.Execute("update online_device set ExpTime=@ExpTime where OnlineID=@OnlineID;", new { OnlineID = onlineId, ExpTime = exptime }, transaction);
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        // 发生异常时回滚事务
                        transaction.Rollback();
                        // 处理异常
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }
        public void Dispose()
        {
            repo.Dispose();
        }
    }
}
